package app.framework.security.service;

import app.framework.constant.AuthConstants;
import app.framework.security.util.LoginAttemptCountUtil;
import app.modules.account.persistence.entity.UserEO;
import app.modules.account.persistence.mapper.UserMapper;
import jasmine.framework.common.util.CollectionUtil;
import jasmine.framework.common.util.NewUtil;
import jasmine.framework.context.InitSupport;
import jasmine.framework.context.RuntimeProvider;
import jasmine.security.authorization.RoleAuthority;
import jasmine.security.rbac.dao.SecRoleDAO;
import jasmine.security.rbac.model.SecRole;
import jasmine.security.subject.ClientSubject;
import jasmine.security.subject.ClientSubjectDetailsService;
import jasmine.security.subject.UserSubject;
import jasmine.security.subject.UserSubjectDetailsService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientRegistrationException;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 整合 Spring Security，实现查询用户信息等接口。
 * </p>
 *
 * @author mh.z
 */
public class DaoSecuritySubjectService implements UserSubjectDetailsService,
        ClientSubjectDetailsService, InitSupport {
    private UserMapper userMapper;
    private SecRoleDAO roleDAO;
    private PasswordEncoder passwordEncoder;

    @Override
    public void init(RuntimeProvider provider) {
        this.userMapper = provider.getByType(UserMapper.class);
        this.passwordEncoder = provider.getByType(PasswordEncoder.class);
        this.roleDAO = provider.getByType(SecRoleDAO.class);
    }

    @Override
    public UserSubject loadUserByUsername(String username) throws UsernameNotFoundException {
        UserEO user = userMapper.getAllTenantUserByName(username);
        if (user == null) {
            throw new UsernameNotFoundException("username '" + username + "' not found");
        }

        // 获取角色
        Long userId = user.getId();
        List<GrantedAuthority> authorityList = getGrantedAuthorities(userId);
        // 用户数据
        Map<String, Object> data = NewUtil.map();
        data.put(AuthConstants.SUBJECT_DATA_USER_TYPE, user.getUserType());

        // 获取该用户重试登录失败的次数并判断是否应该锁定
        int attemptCount = LoginAttemptCountUtil.getAttemptCount(username);
        boolean accountLocked = (attemptCount >= 5);

        UserDetails userDetails = UserSubject.builder()
                .username(user.getUserName())
                .password(getEncodedPassword(user.getPassword()))
                .authorities(authorityList)
                .accountLocked(accountLocked)
                .build();
        UserSubject userSubject = new UserSubject(user.getTenantId(), userId, userDetails, data);

        return userSubject;
    }

    @Override
    public UserSubject loadUserByUserId(Long userId) throws UsernameNotFoundException {
        // 获取用户
        UserEO user = userMapper.getAllTenantUserById(userId);
        if (user == null) {
            throw new UsernameNotFoundException("userId '" + userId + "' not found");
        }

        // 获取角色
        List<GrantedAuthority> authorityList = getGrantedAuthorities(userId);

        UserSubject userDetails = new UserSubject(user.getTenantId(), userId,
                user.getUserName(), getEncodedPassword(user.getPassword()), authorityList);

        return userDetails;
    }

    @Override
    public ClientSubject loadClientByClientId(String clientId) throws ClientRegistrationException {
        UserEO user = userMapper.getAllTenantUserByName(clientId);
        if (user == null) {
            return null;
        }

        // 获取角色
        Long userId = user.getId();
        List<GrantedAuthority> authorityList = getGrantedAuthorities(userId);

        ClientSubject clientDetails = new ClientSubject();
        clientDetails.setClientId(clientId);
        clientDetails.setClientSecret(getEncodedPassword(user.getPassword()));
        clientDetails.setAuthorities(authorityList);
        clientDetails.setAuthorizedGrantTypes(Arrays.asList("authorization_code", "password", "refresh_token"));
        // "CLIENT"是自定义，没有特殊含义
        clientDetails.setScope(Arrays.asList("CLIENT"));
        clientDetails.setAccessTokenValiditySeconds(3600);

        return clientDetails;
    }

    /**
     * 查找指定用户已授予的角色
     *
     * @param userId
     * @return
     */
    protected List<GrantedAuthority> getGrantedAuthorities(Long userId) {
        // 获取角色
        List<SecRole> roleList = roleDAO.listAllTenantRolesByUserIdNoI18n(userId);

        List<GrantedAuthority> authorityList = CollectionUtil.mapToList(roleList, (role) -> {
            return new RoleAuthority(role.getId(), role.getRoleCode());
        });

        return authorityList;
    }

    /**
     * 返回加密后的密码
     *
     * @param password
     * @return
     */
    protected String getEncodedPassword(String password) {
        return password;
    }

}
